﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.ComponentModel;
using BesAsm.Stormwater.TimeSeries;

namespace BesAsm.Stormwater.Swmm5Results
{  
  public abstract class ResultVariablesBase : IEnumerable<ResultVariable>
  {
    public BackgroundWorker Worker { get; set; }
    
    protected string _name;
    protected OutputFileReader _reader;
    
    protected KeyedResultVariableCollection _variables
     = new KeyedResultVariableCollection();

    public int? ReadStatusReportInterval { get; set; }      

    public void LoadAll()
    {
      foreach (ResultVariable rv in this)
        rv.SetData(LoadResult(rv.Position, false));
      _reader.CloseReader();
    }

    public void Load(string variableName, bool close, DateTime startDate, DateTime endDate)
    {      
      if (!_variables.Contains(variableName))
        throw new System.Collections.Generic.KeyNotFoundException();

      ResultVariable rv = _variables[variableName];
      rv.SetData(LoadResult(rv.Position, close, startDate, endDate));
      rv.IsLoaded = true;
    }

    public void Load(string variableName, bool close)
    {
      Load(variableName, close, _reader.StartDate, _reader.EndDate);      
    }

    protected abstract int GetElementPosition();

    protected IDictionary<DateTime, float> LoadResult(int var, bool closeReader)
    {
      return LoadResult(var, closeReader, _reader.StartDate, _reader.EndDate);
    }

    protected IDictionary<DateTime, float> LoadResult(int var, bool closeReader, DateTime startDate, DateTime endDate)
    {
      Dictionary<DateTime, float> dict = new Dictionary<DateTime, float>();

      int reportInterval = ReadStatusReportInterval ?? 100;
      int count = 0;

      try
      {        
        if (startDate < _reader.StartDate + _reader.Interval)
          startDate = _reader.StartDate + _reader.Interval;
        if (endDate > _reader.EndDate)
          endDate = _reader.EndDate;

        int startIndex = CalculateIndex(startDate, _reader.StartDate + _reader.Interval, _reader.Interval);
        int endIndex = CalculateIndex(endDate, _reader.StartDate + _reader.Interval, _reader.Interval);
        
        DateTime dt = startDate;

        long position = GetElementPosition() + (int)var + ((long)_reader.BytesPerRow * (long)startIndex);

        if (!_reader.IsOpen)
          _reader.OpenReader();
        
        byte[] bytes = new byte[4];
        int offset = _reader.BytesPerRow - 4;
        
        for (int i = startIndex; i <= endIndex; i++)
        {
          try
          {
            _reader.Seek(position);                       
            float result  = _reader.ReadSingle();
            dict.Add(dt, result);
            dt += _reader.Interval;
            position += _reader.BytesPerRow;            
          }
          catch (Exception ex)
          {
          }
          count++;
          if (count == reportInterval)
          {
            count = 0;
            if (Worker != null && Worker.CancellationPending)
            {              
              _reader.CloseReader();
              throw new OperationCanceledException("Load results canceled");
            }
            if (Worker != null)
              Worker.ReportProgress((int)(i * 100 / (double)endIndex));
          }
        }
        return dict;
      }
      finally
      {
        if (closeReader)
          _reader.CloseReader();
      }     
    }   
    
    public void Close()
    {
      if (_reader.IsOpen)
        _reader.CloseReader();
    }

    public string Name
    {
      get { return _name; }
    }    

    public ResultVariable this[string name]
    {
      get { return _variables[name]; }
    }

    private int CalculateIndex(DateTime dt, DateTime refDt, TimeSpan interval)
    {
      int i = 0;
      TimeSpan delta = refDt.Subtract(dt);
      return (int)Math.Abs(delta.TotalSeconds) / (int)interval.TotalSeconds;
    }

    #region IEnumerable<ResultVariable> Members

    public IEnumerator<ResultVariable> GetEnumerator()
    {
      return _variables.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
      return _variables.GetEnumerator();
    }

    #endregion
  }
}
